/////////////////////////////////////////////////////
// File Name: mac_t_v1.v
// Author: zeping fan
// mail:   zpfan007@163.com
// Created Time: 2023年06月09日 星期五 10时28分59秒
/////////////////////////////////////////////////////


module  mac_t#(
parameter   DATA_FIFO_DEPTH =   4096,
            FIFO_PTR_WIDTH  =   $clog2(DATA_FIFO_DEPTH),
            DATA_WIDTH      =   8,
            PTR_WIDTH       =   16,
            MAX_LENGTH      =   1518,
            MIN_LENGTH      =   64,
            MAX_FRAME_WIDTH =   11,
            PRE_CNT         =   8,
            CRC_CNT         =   4,
            WAIT_CNT        =   22,  //each pkt need to wait 24 cycles,but there are two extra wait state:IDLE & READY state , so WAIT state only need to keep 22 cycles.
            DROP_CNT_WIDTH  =   16
)(
//system interface

input                               clk,
input                               rst_n,

//MII interface
input                               tx_clk,
output  reg                         tx_dv,
output  reg [3:0]                   tx_d,
output  reg [DROP_CNT_WIDTH-1:0]    cnt_drop,           //drop掉的帧数
//qm interface
output  reg                         qm_dfifo_rd,
input       [DATA_WIDTH-1:0]        qm_dfifo_dout,
output  reg                         qm_pfifo_rd,
input       [PTR_WIDTH-1:0]         qm_pfifo_dout,
input                               qm_pfifo_empty
);
   

//前级数据帧读写状态机
localparam  R_IDLE  =   6'b000001;
localparam  R_PRE   =   6'b000010;
localparam  R_TRANS =   6'b000100;
localparam  R_PAD   =   6'b001000;
localparam  R_CRC   =   6'b010000;
localparam  R_DROP  =   6'b100000;

//MII接口发送数据帧状态机
localparam  T_IDLE  =   4'b0001;
localparam  T_READY =   4'b0010;
localparam  T_TRANS =   4'b0100;
localparam  T_WAIT  =   4'b1000;


//mac_t接收前级数据状态机
reg     [5:0]                   cur_sta_r;
reg     [5:0]                   nxt_sta_r;
reg     [MAX_FRAME_WIDTH-1:0]   cnt_r;

//mac_t向后级发送数据状态机
reg     [3:0]                   cur_sta_t;
reg     [3:0]                   nxt_sta_t;
reg     [MAX_FRAME_WIDTH:0]     cnt_t;      //以半字节为单位

//mac_t内部data fifo变量
reg                             in_data_fifo_wr;
reg     [DATA_WIDTH-1:0]        in_data_fifo_din;
wire                            in_data_fifo_rd;
wire    [DATA_WIDTH-1:0]        in_data_fifo_dout;
wire    [FIFO_PTR_WIDTH:0]      in_data_fifo_wr_cnt;

//mac_t内部ptr fifo变量
reg                             in_ptr_fifo_wr;
wire    [PTR_WIDTH-1:0]         in_ptr_fifo_din;
wire                            in_ptr_fifo_rd;
wire    [PTR_WIDTH-1:0]         in_ptr_fifo_dout;
wire                            in_ptr_fifo_full;
wire                            in_ptr_fifo_empty;

//padding num
reg     [5:0]                   pad_num;

wire                            bp;                 //反压信号，当内部data fifo空间不足以存入一个最大帧时，反压上级，拒绝接收数据
wire                            in_fifo_wr_vld;     //前级数据可以输入内部fifo标志

//crc变量
reg                             crc_init;
wire                            crc_vld;
wire                            crc_calc;
wire    [DATA_WIDTH-1:0]        crc_din;
wire    [DATA_WIDTH-1:0]        crc_dout;

assign  bp = in_ptr_fifo_full || (in_data_fifo_wr_cnt>DATA_FIFO_DEPTH-MAX_LENGTH-8);

assign in_fifo_wr_vld = ~bp & ~qm_pfifo_empty;

//=============================前级状态机===========================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cur_sta_r[5:0] <= R_IDLE;
    else
        cur_sta_r[5:0] <= nxt_sta_r[5:0];
end

always @(*)begin
    case(cur_sta_r)
        R_IDLE: nxt_sta_r[5:0] = ~qm_pfifo_empty? bp? R_DROP : R_PRE : R_IDLE;
        R_PRE:  nxt_sta_r[5:0] = (cnt_r[MAX_FRAME_WIDTH-1:0]==11'b1)? R_TRANS : R_PRE;
        R_TRANS:nxt_sta_r[5:0] = (cnt_r[MAX_FRAME_WIDTH-1:0]==11'b1)? (qm_pfifo_dout[10:0]<MIN_LENGTH-CRC_CNT)? R_PAD : R_CRC : R_TRANS;
        R_PAD:  nxt_sta_r[5:0] = (cnt_r[MAX_FRAME_WIDTH-1:0]==11'b1)? R_CRC : R_PAD;
        R_CRC:  nxt_sta_r[5:0] = (cnt_r[MAX_FRAME_WIDTH-1:0]==11'b1)? R_IDLE : R_CRC;
        R_DROP: nxt_sta_r[5:0] = (cnt_r[MAX_FRAME_WIDTH-1:0]==11'b1)? R_IDLE : R_DROP;
        default:nxt_sta_r[5:0] = R_IDLE;
    endcase
end

//============================计数器================================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt_r[MAX_FRAME_WIDTH-1:0] <= {MAX_FRAME_WIDTH{1'b0}};
    else begin
        case(cur_sta_r)
            R_IDLE: cnt_r[MAX_FRAME_WIDTH-1:0] <= (nxt_sta_r == R_DROP)? qm_pfifo_dout[10:0] : PRE_CNT;
            R_PRE:  cnt_r[MAX_FRAME_WIDTH-1:0] <= (cnt_r[10:0]>11'b1)? (cnt_r[10:0]-1'b1) : qm_pfifo_dout[10:0];
            R_TRANS:cnt_r[MAX_FRAME_WIDTH-1:0] <= (cnt_r[10:0]>11'b1)? (cnt_r[10:0]-1'b1) : (qm_pfifo_dout[10:0]<MIN_LENGTH-CRC_CNT)? (MIN_LENGTH-CRC_CNT-qm_pfifo_dout[10:0]) : CRC_CNT;
            R_PAD:  cnt_r[MAX_FRAME_WIDTH-1:0] <= (cnt_r[10:0]>11'b1)? (cnt_r[10:0]-1'b1) : CRC_CNT;
            R_CRC:  cnt_r[MAX_FRAME_WIDTH-1:0] <= cnt_r[10:0]-1'b1;
            R_DROP: cnt_r[MAX_FRAME_WIDTH-1:0] <= cnt_r[10:0]-1'b1;
            default:cnt_r[MAX_FRAME_WIDTH-1:0] <= {MAX_FRAME_WIDTH{1'b0}};
        endcase
    end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt_drop[DROP_CNT_WIDTH-1:0] <= {DROP_CNT_WIDTH{1'b0}};
    else if(cur_sta_r==R_IDLE && nxt_sta_r==R_DROP)
        cnt_drop[DROP_CNT_WIDTH-1:0] <= cnt_drop + 1'b1;
end

//============================CRC控制================================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        crc_init <= 1'b0;
    else if(cur_sta_r[5:0]==R_IDLE && in_fifo_wr_vld)
        crc_init <= 1'b1;
    else
        crc_init <= 1'b0;
end

assign crc_vld = (cur_sta_r[5:0]==R_TRANS) | (cur_sta_r[5:0]==R_PAD);

assign crc_calc = (cur_sta_r[5:0]==R_TRANS) | (cur_sta_r[5:0]==R_PAD) | (cur_sta_r[5:0]==R_CRC);

assign crc_din[DATA_WIDTH-1:0] = qm_dfifo_dout[DATA_WIDTH-1:0];


//============================qm_fifo数据读取========================
//data_fifo读使能
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        qm_dfifo_rd <= 1'b0;
    else if((cur_sta_r[5:0]==R_PRE && cnt_r[10:0]==11'd1) || (nxt_sta_r[5:0]==R_DROP))    //rd需要提前一拍，dout数据会慢一拍出
        qm_dfifo_rd <= 1'b1;
    else if((cur_sta_r[5:0]==R_TRANS && cnt_r[10:0]==11'd1) || (nxt_sta_r[5:0]==R_IDLE))
        qm_dfifo_rd <=1'b0;
end

//ptr_fifo读使能
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        qm_pfifo_rd <= 1'b0;
    else if((cur_sta_r[5:0]==R_CRC && cnt_r[10:0]==11'd2) || (cur_sta_r[5:0]==R_DROP && cnt_r[10:0]==11'd2))
        qm_pfifo_rd <= 1'b1;
    else
        qm_pfifo_rd <= 1'b0;
end

//=========================interal fifo控制========================== 
//interal data_fifo_wr
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        in_data_fifo_wr <= 1'b0;
    else if(cur_sta_r[5:0]==R_IDLE)
        in_data_fifo_wr <= 1'b0;    
    else if(cur_sta_r[5:0]==R_PRE)
        in_data_fifo_wr <= 1'b1;
end

//interal data_fifo_din
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        in_data_fifo_din[DATA_WIDTH-1:0] <= {DATA_WIDTH{1'b0}};
    else begin
        case(cur_sta_r)
            R_IDLE: in_data_fifo_din[DATA_WIDTH-1:0] <= 8'h55;
            R_PRE:  in_data_fifo_din[DATA_WIDTH-1:0] <= (cnt_r[10:0]>11'b1)? 8'h55 : 8'hd5;
            R_TRANS:in_data_fifo_din[DATA_WIDTH-1:0] <= qm_dfifo_dout[DATA_WIDTH-1:0];
            R_PAD:  in_data_fifo_din[DATA_WIDTH-1:0] <= {DATA_WIDTH{1'b0}};
            R_CRC:  in_data_fifo_din[DATA_WIDTH-1:0] <= crc_dout[DATA_WIDTH-1:0];
        endcase
    end
end

//interal ptr_fifo wr
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        in_ptr_fifo_wr <= 1'b0;
    else if(cur_sta_r[5:0]==R_CRC && cnt_r[10:0]==11'd2)    //fifo内部写数据时已经有full信号控制
        in_ptr_fifo_wr <= 1'b1;
    else 
        in_ptr_fifo_wr <= 1'b0;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        pad_num[5:0] <= 6'b0;
    else if(cur_sta_r == R_TRANS && nxt_sta_r == R_PAD)
        pad_num[5:0] <= MIN_LENGTH-CRC_CNT-qm_pfifo_dout[10:0];
    else
        pad_num[5:0] <= 6'b0;
end

assign in_ptr_fifo_din[15:0] = qm_pfifo_dout[15:0] + pad_num[5:0];

//===================================================================
//                             数据发送状态机
//===================================================================

assign in_ptr_fifo_rd = (cur_sta_t[3:0]==T_IDLE) && !in_ptr_fifo_empty;

assign in_data_fifo_rd = (cur_sta_t[3:0]==T_READY) | (cur_sta_t[3:0]==T_TRANS && cnt_t[0] && cnt_t[11:0]>12'b1);

always @(posedge tx_clk or negedge rst_n)begin
    if(!rst_n)
        cur_sta_t[3:0] <= T_IDLE;
    else
        cur_sta_t[3:0] <= nxt_sta_t[3:0];
end

always @(*)begin
    case(cur_sta_t[3:0])
        T_IDLE: nxt_sta_t[3:0] = (!in_ptr_fifo_empty)?  T_READY : T_IDLE;
        T_READY:nxt_sta_t[3:0] = T_TRANS;
        T_TRANS:nxt_sta_t[3:0] = (cnt_t[11:0]==12'b1)?  T_WAIT  : T_TRANS;
        T_WAIT: nxt_sta_t[3:0] = (cnt_t[11:0]==12'b1)?  T_IDLE  : T_WAIT;
        default:nxt_sta_t[3:0] = T_IDLE;
    endcase
end

always @(posedge tx_clk or negedge rst_n)begin
    if(!rst_n)
        cnt_t[11:0] <= 12'b0;
    else begin
        case(cur_sta_t[3:0])
            T_IDLE: cnt_t[11:0] <= 12'b0;
            T_READY:cnt_t[11:0] <= (in_ptr_fifo_dout[10:0]+PRE_CNT+CRC_CNT)<<1'b1;
            T_TRANS:cnt_t[11:0] <= (cnt_t[11:0]==12'b1)? WAIT_CNT : cnt_t[11:0]-1'b1;
            T_WAIT: cnt_t[11:0] <= cnt_t[11:0]-1'b1;
            default:cnt_t[11:0] <= 12'b0;
        endcase
    end
end

always @(posedge tx_clk or negedge rst_n)begin
    if(!rst_n)
        tx_dv <= 1'b0;
    else if(cur_sta_t[3:0]==T_TRANS)
        tx_dv <= 1'b1;
    else
        tx_dv <= 1'b0;
end

always @(posedge tx_clk or negedge rst_n)begin
    if(!rst_n)
        tx_d[3:0] <= 4'b0;
    else if(!cnt_t[0])
        tx_d[3:0] <= in_data_fifo_dout[3:0];
    else
        tx_d[3:0] <= in_data_fifo_dout[7:4];
end



crc32    
x_crc32_t_v1(
    .clk(clk),
    .rstn(rst_n),
    .data(crc_din),
    .vld(crc_vld),
    .init(crc_init),
    .calc(crc_calc),
    .crc_reg(),
    .crc(crc_dout)
    );

asyn_fifo#(
    .FIFO_DEPTH(DATA_FIFO_DEPTH),
    .DATA_WIDTH(8),
    .DATA_FLOAT_OUT(0)
)
    x_data_fifo(
    .rstn_i(rst_n),
    .wr_clk_i(clk),
    .wr_en_i(in_data_fifo_wr),
    .wr_data_i(in_data_fifo_din),
    .wr_full_o(),
    .wr_cnt_o(in_data_fifo_wr_cnt),
    .rd_clk_i(tx_clk),
    .rd_en_i(in_data_fifo_rd),
    .rd_data_o(in_data_fifo_dout),
    .rd_empty_o(),
    .rd_cnt_o()
    );


asyn_fifo#(
    .FIFO_DEPTH(32),
    .DATA_WIDTH(16),
    .DATA_FLOAT_OUT(0)
)
    x_ptr_fifo(
    .rstn_i(rst_n),
    .wr_clk_i(clk),
    .wr_en_i(in_ptr_fifo_wr),
    .wr_data_i(in_ptr_fifo_din),
    .wr_full_o(in_ptr_fifo_full),
    .wr_cnt_o(),
    .rd_clk_i(tx_clk),
    .rd_en_i(in_ptr_fifo_rd),
    .rd_data_o(in_ptr_fifo_dout),
    .rd_empty_o(in_ptr_fifo_empty),
    .rd_cnt_o()
    );


endmodule
